package org.bdware.irp.client;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.nimbusds.jose.jwk.JWK;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.log4j.Logger;
import org.bdware.irp.crypto.CertUtils;
import org.bdware.irp.exception.IrpClientException;
import org.bdware.irp.stateinfo.DoStateInfo;
import org.bdware.irp.stateinfo.StateInfoBase;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.util.*;

public class CdiIrpClient implements IrpClient{

    JWK clientKeyPair;
    String LHSUrl;
    static Logger logger = Logger.getLogger(CdiIrpClient.class);
    String ClientID;

    public CdiIrpClient(JWK kp, String clientID, String LHSUrl){
        this.ClientID = clientID;
        this.LHSUrl = LHSUrl;
    }

    @Override
    public StateInfoBase resolve(String handle) throws IrpClientException {
        String response = resolveFromLHS(handle);
        JsonObject resp = new Gson().fromJson(response,JsonObject.class);
        StateInfoBase hr = new StateInfoBase();
        if(resp == null ||!resp.has("identifier")
                || (resp.has("status"))){
            throw new IrpClientException(response);
        }
        hr.identifier = resp.get("identifier").getAsString();
        hr.handleValues = resp;
        return hr;
    }

    @Override
    public String register(StateInfoBase hr) throws IrpClientException {
        Map<String, String> map = new HashMap<>();
        for(String keys:hr.handleValues.keySet()){
            map.put(keys,hr.handleValues.get(keys).getAsString());
        }
        map.remove("identifier");
        map.put("action","forceRegister");
        String response = signAndSend(hr,map);
        JsonObject resp = new Gson().fromJson(response,JsonObject.class);
        if(resp == null || !resp.has("identifier")){
            throw new IrpClientException(response);
        }
        return resp.get("identifier").getAsString();
    }

    @Override
    public String reRegister(StateInfoBase hr) throws IrpClientException {
        if(hr.identifier == null){
            logger.warn("handle need to be set.");
        }
        Map<String, String> map = new HashMap<>();
        for(String keys:hr.handleValues.keySet()){
            map.put(keys,hr.handleValues.get(keys).getAsString());
        }
        map.put("identifier",hr.identifier);
        map.put("action","forceRegister");
        String response = signAndSend(hr,map);
        JsonObject resp = new Gson().fromJson(response,JsonObject.class);
        if(resp == null ||!resp.has("identifier")){
            throw new IrpClientException(response);
        }
        return resp.get("identifier").getAsString();
    }

    @Override
    public String unRegister(String handle) {
        Map<String, String> map = new HashMap<>();
        map.put("identifier",handle);
        map.put("action","forceDelete");
        DoStateInfo hr = new DoStateInfo("","");
        hr.identifier = handle;
        return signAndSend(hr, map);
    }

    @Override
    public List<String> batchRegister(StateInfoBase hr, int count){
        return null;
    }

    private String signAndSend(StateInfoBase hr, Map<String, String> map) {
        map.put("dou",this.ClientID);
        String reqMapStr = MapToString(map);
        if(clientKeyPair != null){
            try {
                String signature = CertUtils.Sign(reqMapStr.getBytes(), clientKeyPair);
                map.put("signature",signature);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return sendPost(map, LHSUrl + getSubUrl(hr));
    }


    public String MapToString(Map<String,String> reqMap){
        Set<String> keySet = reqMap.keySet();
        keySet.remove("signature");
        //升序
        Set<String> sortSet = new TreeSet<>();
        sortSet.addAll(keySet);
        String mapString = "";
        for(String key:sortSet){
            if(reqMap.get(key) != null)
                mapString += reqMap.get(key);
        }
        return mapString;
    }

    public String resolveFromLHS(String id){
        String result;
        String resolveUrl = LHSUrl + "resolve?identifier=" + id;
        HttpGet httpGet = new HttpGet(resolveUrl);
        try{
            CloseableHttpClient httpClient = HttpClients.createDefault();

            HttpResponse response = httpClient.execute(httpGet);
            InputStream in = response.getEntity().getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            br.close();
            in.close();
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!= HttpStatus.SC_OK){
                result = "服务器异常";
            }
        } catch (Exception e){
            System.out.println("请求异常");
            throw new RuntimeException(e);
        }
        return result;
    }

    public String sendPost(Map<String, String> reqMap, String url){
        String result;
        HttpPost post = new HttpPost(url);
        try{
            CloseableHttpClient httpClient = HttpClients.createDefault();
            List<BasicNameValuePair> params = new ArrayList<>();
            for(String keys:reqMap.keySet()){
                params.add(new BasicNameValuePair(keys,reqMap.get(keys)));
            }
            UrlEncodedFormEntity postForm = new UrlEncodedFormEntity(params,"utf-8");
            post.setEntity(postForm);
            HttpResponse response = httpClient.execute(post);

            InputStream in = response.getEntity().getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(in, "utf-8"));
            StringBuilder strber= new StringBuilder();
            String line = null;
            while((line = br.readLine())!=null){
                strber.append(line+'\n');
            }
            br.close();
            in.close();
            result = strber.toString();
            if(response.getStatusLine().getStatusCode()!= HttpStatus.SC_OK){
                result = "服务器异常";
            }
        } catch (Exception e){
            System.out.println("请求异常");
            throw new RuntimeException(e);
        } finally{
            post.abort();
        }
        return result;
    }

    private String getSubUrl(StateInfoBase hr){
        String className = hr.getClass().getSimpleName();
        String subUrl;
        switch (className){
            case "DoHandleRecord":
                subUrl = "do/";
                break;
            case "DoipServiceHandleRecord":
                subUrl = "service/";
                break;
            case "UserHandleRecord":
                subUrl = "dou/";
                break;
            default:
                logger.error("use specific handle record class instead.");
                subUrl = "";
                break;
        }
        return subUrl;
    }
}
